package keer.httpinterface.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 注解工具类，提供了一些辅助方法来操作和检查Java注解
 * @see java.lang.annotation.Annotation
 * @see AnnotationInfo
 *
 * @author Keer
 * @since 1.0.0
 */
public final class AnnotationUtils {
    private final static Map<Class<?>, AnnotationInfo[]> CLASS_ANNOTATION_CACHE = new ConcurrentHashMap<>();

    private AnnotationUtils() {

    }

    /**
     * 获取指定类的所有声明注解信息
     * 此方法首先检查类注解缓存以获取注解信息，如果缓存中不存在，则从类中提取注解信息，
     * 并将其转换为AnnotationInfo对象数组，最后将这些信息添加到缓存中以供后续使用
     *
     * @param clazz 要获取注解信息的类
     * @return AnnotationInfo[] 包含类的所有声明注解信息的数组
     */
    public static AnnotationInfo[] getDeclaredAnnotations(Class<?> clazz) {
        // 检查缓存中是否已存在该类的注解信息
        if (CLASS_ANNOTATION_CACHE.containsKey(clazz)) {
            return CLASS_ANNOTATION_CACHE.get(clazz);
        }
        // 同步代码块，防止多线程环境下对缓存的并发修改
        synchronized (CLASS_ANNOTATION_CACHE) {
            // 再次检查缓存，以防其他线程在等待期间已添加注解信息
            if (CLASS_ANNOTATION_CACHE.containsKey(clazz)) {
                return CLASS_ANNOTATION_CACHE.get(clazz);
            }
            // 从类中提取声明注解
            Annotation[] annotations = clazz.getDeclaredAnnotations();
            // 创建AnnotationInfo对象数组以存储转换后的注解信息
            AnnotationInfo[] annotationInfos = new AnnotationInfo[annotations.length];
            // 遍历注解，将其转换为AnnotationInfo对象
            for (int i = 0; i < annotations.length; i++) {
                AnnotationInfo info = AnnotationInfo.of(annotations[i]);
                annotationInfos[i] = info;
            }
            // 将注解信息添加到缓存中
            CLASS_ANNOTATION_CACHE.put(clazz, annotationInfos);
            // 返回注解信息数组
            return annotationInfos;
        }
    }

    /**
     * 获取类上声明的指定类型注解的信息
     *
     * @param clazz            要检查的类
     * @param annotationType   注解类型
     * @param includeInherited 是否包括继承的注解
     * @return 如果找到注解，则返回注解信息；否则返回null
     */
    public static AnnotationInfo getDeclaredAnnotation(Class<?> clazz, Class<? extends Annotation> annotationType, boolean includeInherited) {
        Annotation[] annotations = clazz.getDeclaredAnnotations();
        return getAnnotationIn(annotations, annotationType, includeInherited);
    }

    /**
     * 获取方法上声明的指定类型注解的信息
     *
     * @param method           要检查的方法
     * @param annotationType   注解类型
     * @param includeInherited 是否包括继承的注解
     * @return 如果找到注解，则返回注解信息；否则返回null
     */
    public static AnnotationInfo getMethodAnnotation(Method method, Class<? extends Annotation> annotationType, boolean includeInherited) {
        Annotation[] annotations = method.getDeclaredAnnotations();
        return getAnnotationIn(annotations, annotationType, includeInherited);
    }

    /**
     * 在给定的注解数组中查找指定类型的注解信息
     *
     * @param annotations      注解数组
     * @param annotationType   注解类型
     * @param includeInherited 是否包括继承的注解
     * @return 如果找到注解，则返回注解信息；否则返回null
     */
    public static AnnotationInfo getAnnotationIn(Annotation[] annotations, Class<? extends Annotation> annotationType, boolean includeInherited) {
        if (CollectionUtils.isEmpty(annotations)) {
            return null;
        }
        for (int i = annotations.length - 1; i >= 0; i--) {
            AnnotationInfo info = AnnotationInfo.of(annotations[i]);
            if (info.getAnnotationType().equals(annotationType)) {
                return info;
            }
            AnnotationInfo result = info.getDeclaredAnnotation(annotationType, includeInherited);
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    /**
     * 检查指定类上是否呈现了特定的注解
     *
     * @param clazz            要检查的类
     * @param annotationClass  要查找的注解类
     * @param includeInherited 是否包括继承的注解
     * @return 如果找到了指定的注解，则返回true；否则返回false
     */
    public static boolean isAnnotationPresent(Class<?> clazz, Class<? extends Annotation> annotationClass, boolean includeInherited) {
        // 在类声明的注解中查找指定的注解信息
        AnnotationInfo info = getAnnotationIn(clazz.getDeclaredAnnotations(), annotationClass, includeInherited);
        // 如果找到了指定的注解信息，则返回true，否则返回false
        return info != null;
    }

    /**
     * 获取类上的指定注解
     *
     * @param clazz           要检查的类
     * @param annotationClass 注解的类类型
     * @param <T>             泛型参数，表示注解类型
     * @return 如果类上存在指定的注解，则返回该注解，否则返回null
     */
    public static <T extends Annotation> T getAnnotation(Class<?> clazz, Class<T> annotationClass) {
        return clazz.getAnnotation(annotationClass);
    }

    /**
     * 获取方法上的指定注解
     * 该方法用于反射获取某个方法上是否包含特定的注解，并返回该注解实例
     * 主要用于在运行时动态检查方法是否具有某些元数据，并据此调整程序行为
     *
     * @param method          要检查的方法对象，通过反射获取
     * @param annotationClass 注解的类对象，指定需要查找的注解类型
     * @param <T>             泛型参数，表示注解的类型
     * @return 如果方法上有指定的注解，则返回该注解的实例；否则返回null
     */
    public static <T extends Annotation> T getAnnotation(Method method, Class<T> annotationClass) {
        return method.getAnnotation(annotationClass);
    }

    /**
     * 获取具有指定注解的类型集合
     *
     * @param classes          待检查的类集合
     * @param annotationClass  注解类型
     * @param includeInherited 是否包含继承的注解
     * @return 具有指定注解的类型集合，如果没有类型匹配则返回null
     */
    public static Set<Class<?>> getAnnotationPresentTypes(Collection<Class<?>> classes, Class<? extends Annotation> annotationClass, boolean includeInherited) {
        // 初始化类型集合，根据classes参数的存在与否进行操作
        Set<Class<?>> types = null;
        // 确保传入的类集合不为空
        if (classes != null) {
            for (Class<?> clz : classes) {
                // 检查类是否具有指定注解
                if (isAnnotationPresent(clz, annotationClass, includeInherited)) {
                    // 懒加载类型集合，仅在找到匹配类型时初始化
                    if (types == null) {
                        types = new LinkedHashSet<Class<?>>();
                    }
                    // 将匹配的类型添加到集合中
                    types.add(clz);
                }
            }
        }
        // 返回具有指定注解的类型集合，如果没有匹配的类型则返回null
        return types;
    }

    /**
     * 检查给定方法上是否具有指定的注解
     *
     * @param method           要检查的方法
     * @param annotationClass  注解的类，用于检查是否存在
     * @param includeInherited 是否包括继承的注解
     * @return 如果方法上存在指定的注解，则返回true；否则返回false
     */
    public static boolean isAnnotationPresent(Method method, Class<? extends Annotation> annotationClass, boolean includeInherited) {
        // 获取方法上声明的所有注解
        Annotation[] annotations = method.getDeclaredAnnotations();
        for (Annotation a : annotations) {
            // 递归检查注解及其成员是否与指定注解匹配
            if (isAnnotationPresent(a, annotationClass, includeInherited)) {
                // 如果找到指定注解，返回true
                return Boolean.TRUE;
            }
        }
        // 如果未找到指定注解，返回false
        return Boolean.FALSE;
    }

    /**
     * 检查给定注解上是否呈现了指定的注解类型.
     *
     * @param annotation       要检查的注解实例.
     * @param annotationClass  要检查的注解类型.
     * @param includeInherited 是否包括继承的注解在检查中.
     * @return 如果找到了指定的注解类型，则返回true；否则返回false.
     */
    public static boolean isAnnotationPresent(Annotation annotation, Class<? extends Annotation> annotationClass, boolean includeInherited) {
        // 获取给定注解的类型上声明的所有注解
        Annotation[] aa = annotation.annotationType().getDeclaredAnnotations();
        for (Annotation a : aa) {
            // 检查当前注解是否是目标注解类型
            if (a.annotationType().equals(annotationClass)) {
                return Boolean.TRUE;
            } else if (includeInherited) {
                // 如果包括继承的注解，递归检查当前注解
                return isAnnotationPresent(a, annotationClass, Boolean.TRUE);
            }
        }
        // 如果没有找到目标注解类型，返回false
        return Boolean.FALSE;
    }

    /**
     * 检查指定字段上是否呈现了给定类型的注解
     *
     * @param field            要检查的字段
     * @param annotationClass  要检查的注解类型
     * @param includeInherited 是否包括继承的注解
     * @return 如果指定字段上呈现了给定类型的注解，则返回true；否则返回false
     */
    public static boolean isAnnotationPresent(Field field, Class<? extends Annotation> annotationClass, boolean includeInherited) {
        return getAnnotationIn(field.getDeclaredAnnotations(), annotationClass, includeInherited) != null;
    }

    /**
     * 获取指定类中包含特定注解的方法集合
     *
     * @param clazz                要检查的类
     * @param annotationClass      要查找的注解类型
     * @param containsSuperclasses 是否包含超类的方法
     * @param includeInherited     是否包括继承的注解
     * @return 包含特定注解的方法集合，如果没有找到则返回null
     */
    public static Set<Method> getAnnotatedMethods(Class<?> clazz, Class<? extends Annotation> annotationClass, boolean containsSuperclasses, boolean includeInherited) {
        // 获取类中声明的方法，根据containsSuperclasses参数决定是否包含超类的方法
        Method[] methods = ClassUtils.getDeclaredMethods(clazz, containsSuperclasses);
        Set<Method> annotatedMethods = null;
        // 遍历方法数组，检查每个方法是否包含指定的注解
        for (Method method : methods) {
            // 检查方法是否包含指定注解，根据includeInherited参数决定是否包括继承的注解
            if (isAnnotationPresent(method, annotationClass, includeInherited)) {
                // 如果是第一个找到的注解方法，初始化集合
                if (annotatedMethods == null) {
                    annotatedMethods = new LinkedHashSet<>();
                }
                // 将注解方法添加到集合中
                annotatedMethods.add(method);
            }
        }
        // 返回包含注解的方法集合，如果没有找到则返回null
        return annotatedMethods;
    }

    /**
     * 获取指定类中声明的字段，这些字段带有特定注解
     *
     * @param clazz             要检查的类
     * @param annotationType    注解类型
     * @param includeAnnotation 是否需要包含带有指定注解的字段
     * @param includeSuper      是否包含父类字段
     * @return 包含指定注解的字段列表
     */
    public static List<Field> getDeclaredFieldsAnnotationPresent(Class<?> clazz,
                                                                 Class<? extends Annotation> annotationType,
                                                                 boolean includeAnnotation,
                                                                 boolean includeSuper) {
        // 初始化用于存储带有指定注解的字段的列表
        List<Field> fields = new ArrayList<>();
        // 获取类（可能包括父类）中声明的所有字段
        List<Field> tmp = ClassUtils.getDeclaredFields(clazz, includeSuper);
        // 遍历所有字段
        for (Field field : tmp) {
            // 检查字段是否带有指定的注解
            if (isAnnotationPresent(field, annotationType, includeAnnotation)) {
                // 如果是，则将字段添加到结果列表中
                fields.add(field);
            }
        }
        // 返回结果列表
        return fields;
    }
}

